;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;							;
;	WMPATCH.ASM					;
;							;
; 	Patch for Word-Master 1.07 to use with		;
; 	the Echelon 'Z' System. This patch causes	;
; 	WordMaster to use the current terminal		;
; 	characteristics defined in Z3TCAP.		;
;							;
; 	Copyright (c) 1985 by Dennis Wright and		;
;	Echelon, Inc.					;
;							;
; 	Contains selected ZCPR3 VLIB routines.	        ;
;							;
; 	VLIB is copyrighted by Richard Conn and		;
; 	Echelon, Inc.					;
;							;
; 	WordMaster is a trademark of MicroPro		;
; 	International.					;
;							;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
	MACLIB	Z3BASE
;
;	WordMaster 1.07 Patch Addresses
;
CLRSCRN	EQU	0180H		; Clearscreen routine
HITE	EQU	01B9H		; Number of lines
WID	EQU	01BAH		; Number of columns
EREOL	EQU	01BBH		; Erase to end of line
NOVIO	EQU	01BDH		; No video i/o board
DELCLR	EQU	01C1H		; Delay after clear screen
DELCUS	EQU	01C1H
DELERE	EQU	01C3H		; Delay for non-cursor set functions
TCURSOR	EQU	0196H		; User cursor positioning routine
PBEGMEM	EQU	01B7H		; Contains beginning of text buffer address
SPEC	EQU	29B8H		; Get this address from PBEGMEM
OUTCHR	EQU	01EFH		; Output character routine.
START	EQU	0269H		; Start of Word-Master Version 1.07
;
	ORG	100H
	JMP	INIT		; Goto our init routine
;
	ORG	CLRSCRN
	CALL	CLS
	LXI	H,0
	JMP	XYPOS		; Put cursor at 0, 0
;
	ORG	TCURSOR		; User-patchable cursor positioning routine.
	JMP	XYPOS		; Use our own cursor positioning routine.
;
	ORG	PBEGMEM
	DW	BEGMEM
;
	ORG	SPEC		; Added patch area
INIT:	LXI	H,Z3ENV		; Point to the ZCPR3 environment
	LXI	D,80H
	SHLD	ENVPTR		; Save pointer
	PUSH	H		; Save for later
	DAD	D		; Displace to Z3TCAP
	SHLD	VIDPTR		; Save pointer
	XRA	A
	LXI	H,DELCLR
	MOV	M,A
	INX	H
	MOV	M,A
	INX	H
	MOV	M,A
	POP	H		; Restore env pointer
	LXI	D,47		; Offset to CRT select byte
	DAD	D
	MOV	A,M		; Get it.
	LXI	D,2
	ORA	A		; Crt 0?
	JZ	GETCRT		; Yes move crt 0 data to wm patch area
	LXI	D,5		; No, move crt 1 data
GETCRT:	DAD	D
	MOV	A,M		; Get tcap width
	STA	WID		; Patch wm width
	INX	H
	MOV	A,M		; Get tcap number of lines
	STA	HITE		; Patch wm hite
	LHLD	VIDPTR		; Get tcap pointer
	LXI	D,22		; Offset to ce delay
	DAD	D
	MOV	A,M		; Get ce delay
	STA	DELERE		; Patch wm delay after erase to eol
	INX	H
	CALL	VIDSKP		; Skip past cl string
	CALL	VIDSKP		; Skip past cm string
	LXI	D,EREOL		; Point to wm eol string
	PUSH	H
	MVI	C,-1
CNTEOL:	MOV	A,M		; Get tcap byte count for eol
	INX	H
	INR	C
	ORA	A
	JNZ	CNTEOL
	POP	H
	MOV	A,C
	CPI	3		; More than 2 bytes?
	JNC	INIT1		; If so don't use it
	CALL	FILL		; No, patch with tcaps eol string
	JMP	INIT2
INIT1:	XRA	A		; Tell wm to simulate eol
	STAX	D
INIT2:	MVI	A,0FFH		; Set no video i/o
	STA	NOVIO
	JMP	START		; We're done
;
FILL:	MOV	A,M		; Replace wm data with ours
	ORA	A
	RZ
	STAX	D
	INX	H
	INX	D
	JMP	FILL
;
COUT:	PUSH	H
	PUSH	D
	CALL	OUTCHR
	POP	D
	POP	H
	RET
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;	The following routines are from VLIB by Richard Conn.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Clear Screen
;	Return with A=0 and Zero Flag Set if not done
;
CLS:	PUSH	H		; Save regs
	PUSH	D
	LHLD	VIDPTR		; Pt to environment
	MOV	A,M		; No terminal?
	CPI	' '+1
	JC	CLSERR
	LXI	D,14H		; Pt to cls delay
	DAD	D
	MOV	D,M		; Get it
	INX	H		; Pt to cls string
	INX	H
	INX	H
	MOV	A,M		; Get first char of string
	ORA	A		; If no string, error
	JZ	CLSERR
	CALL	VIDOUT		; Output string with delay
	POP	D		; Done
	POP	H
	XRA	A		; Return NZ
	DCR	A
	RET
;
CLSERR:	POP	D		; Done
	POP	H
	XRA	A		; Return Z
	RET
;
;  VIDOUT - Output video string pted to by HL
;	Output also a delay contained in the D register
;
VIDOUT:	MOV	A,M		; Get next char
	ORA	A		; Done if zero
	JZ	VIDO2
	INX	H		; Pt to next
	CPI	'\'		; Literal value?
	JNZ	VIDO1
	MOV	A,M		; Get literal char
	INX	H		; Pt to after it
VIDO1:	CALL	COUT		; Output char
	JMP	VIDOUT
VIDO2:	MOV	A,D		; Output delay
	JMP	VIDELAY		; Output delay
;
; GOTO XY	(From VLIB)
;	HL = Row/Col, with Home=1/1
;	Return with A=0 and Zero Flag Set if not done
;
XYPOS:	MOV	D,L		; Swap row & column
	MOV	E,H		;  for VLIB
	XCHG
GOTOXY:	PUSH	B		; Save regs
	PUSH	D
	PUSH	H
	LHLD	VIDPTR		; Pt to environment
	MOV	A,M		; No terminal?
	CPI	' '+1
	JC	ERR
	LXI	D,15H		; Pt to CM delay
	DAD	D
	MOV	A,M		; Get it
	STA	CMDELAY		; Save it
	INX	H		; Pt to CL string
	INX	H
	CALL	VIDSKP		; Skip CL string
	MOV	A,M		; Get first char of CM string
	ORA	A		; If no string, error
	JZ	ERR
	XCHG			; DE=address of CM string
	POP	H		; Get coordinates in HL
	PUSH	H
	CALL	GXY		; Output xy string with delay
	LDA	CMDELAY		; Pause
	CALL	VIDELAY
	POP	H		; Done
	POP	D
	POP	B
	XRA	A		; Return NZ
	DCR	A
	RET
ERR:	POP	H		; Done
	POP	D
	POP	B
	XRA	A		; Return Z
	RET
;
; GOTOXY	(From VLIB)
;   On input, H=Row and L=Column to Position To (1,1 is Home)
;   On input, DE=address of CM string
;
GXY:	XRA	A		; Set row/column
	STA	RCORDER		; Row before column
	STA	RCBASE		; Add 0 to base
;
; Cycle thru string
;
GXYLOOP:LDAX	D		; Get next char
	INX	D		; Pt to next
	ORA	A		; Done?
	RZ
	CPI	'%'		; Command?
	JZ	GXYCMD
	CPI	'\'		; Escape?
	JZ	GXYESC
	CALL	COUT		; Send char
	JMP	GXYLOOP

;
; Escape - output following byte literally
;
GXYESC:	LDAX	D		; Get next char
	CALL	COUT		; Output literally
	INX	D		; Pt to next
	JMP	GXYLOOP
;
; Interpret next character as a command character
;
GXYCMD:	LDAX	D		; Get command char
	INX	D		; Pt to next
	CPI	'd'		; %d
	JZ	GXYOUT1
	CPI	'2'		; %2
	JZ	GXYOUT2
	CPI	'3'		; %3
	JZ	GXYOUT3
	CPI	'.'		; %.
	JZ	GXYOUT4
	CPI	'+'		; %+v
	JZ	GXYOUT5
	CPI	'>'		; %>xy
	JZ	GXYGT
	CPI	'r'		; %r
	JZ	GXYREV
	CPI	'i'		; %i
	JZ	GXYINC
	CALL	COUT		; Output char if nothing else
	JMP	GXYLOOP
;
; Set row/col home to 1,1 rather than 0,0
;
GXYINC:	MVI	A,1		; Set rcbase to 1
	STA	RCBASE
	JMP	GXYLOOP
;
; Reverse order of output to column then row (default is row then column)
;
GXYREV:	MVI	A,1		; Set column and row order
	STA	RCORDER
	JMP	GXYLOOP
;
; Command: >xy
;   If value of row/col is greater than x, add y to it
;
GXYGT:	CALL	GETVAL		; Get value
	MOV	C,A		; Save value
	LDAX	D		; Get value to test
	INX	D		; Pt to next
	CMP	C		; If carry, value>x
	JNC	GXYGT1
	LDAX	D		; Get value to add
	ADD	C
	CALL	PUTVAL		; Put value back
GXYGT1:	INX	D		; Pt to next
	JMP	GXYLOOP		; Resume
;
; Command: +n
;   Add n to next value and output
;
GXYOUT5:LDAX	D		; Get value to add
	INX	D		; Pt to next
	MOV	B,A		; Save in B
	CALL	GETVAL		; Get value
	ADD	B		; Add in B
	CALL	COUT		; Output value
RCMARK:	LDA	RCORDER		; Mark output
	ORI	80H
	STA	RCORDER
	JMP	GXYLOOP
;
; Command: .
;   Output next value
;
GXYOUT4:CALL	GETVAL		; Get value
	CALL	COUT		; Output value
	JMP	RCMARK
;
; Command: 3
;   Output next value as 3 decimal digits
;
GXYOUT3:CALL	GETVAL		; Get value
	MVI	B,100		; Output 100's
	MVI	C,1		; Leading zeroes
	CALL	DIGOUT
GXYOT3:	MVI	B,10		; Output 10's
	MVI	C,1		; Leading zeroes
GXYOT2:	CALL	DIGOUT
	ADI	'0'		; Output 1's
	CALL	COUT
	JMP	RCMARK
;
; Command: 2
;   Output next value as 2 decimal digits
;
GXYOUT2:CALL	GETVAL		; Get value
	JMP	GXYOT3
;
; Command: d
;   Output next value as n decimal digits with no leading zeroes
;
GXYOUT1:CALL	GETVAL		; Get value
	MVI	B,100		; Output 100's
	MVI	C,0		; No leading zeroes
	CALL	DIGOUT
	MVI	B,10		; Output 10's
	MVI	C,0		; No leading zeroes
	JMP	GXYOT2
;
; Return next value in A
;
GETVAL:	LDA	RCORDER		; Get order flag
	ORA	A		; Already output the first value?
	JM	GETVAL2
	ANI	1		; Look at lsb
	JZ	GETVALR		; If 0, row first
GETVALC:LDA	RCBASE		; Get base offset
	ADD	L		; Get column
	RET
;
GETVALR:LDA	RCBASE		; Get base offset
	ADD	H		; Get row
	RET
;
GETVAL2:ANI	1		; Look at lsb
	JZ	GETVALC
	JMP	GETVALR
;
; Store A as next value
;
PUTVAL:	MOV	C,A		; Save value
	LDA	RCORDER		; Get order flag
	ORA	A		; Already output the first value?
	JM	PUTVAL2
	ANI	1		; Look at lsb
	JZ	PUTVALR		; If 0, row first
PUTVALC:MOV	L,C		; Set column
	RET
;
PUTVALR:MOV	H,C		; Set row
	RET
;
PUTVAL2:ANI	1		; Look at lsb
	JZ	PUTVALC
	JMP	PUTVALR
;
; Output A as decimal digit char
;   B=Quantity to Subtract from A, C=0 if no leading zero
;
DIGOUT:	PUSH	D		; Save DE
	MVI	D,'0'		; Char
DECOT1:	SUB	B		; Subtract
	JC	DECOT2
	INR	D		; Increment char
	JMP	DECOT1
;
DECOT2:	ADD	B		; Add back in
	PUSH	PSW		; Save result
	MOV	A,D		; Get digit
	CPI	'0'		; Zero?
	JNZ	DECOT3
	MOV	A,C		; Get zero flag
	ORA	A		; 0=no zero
	JZ	DECOT4
DECOT3:	MOV	A,D		; Get digit
	CALL	COUT		; Print it
DECOT4:	POP	PSW		; Get A
	POP	D		; Restore DE
	RET
;
; GXY Buffers
;
RCORDER:DS	1		; 0=row/col, else col/row
RCBASE:	DS	1		; 0=org is 0,0, else org is 1,1
CMDELAY:DS	1		; Number of milliseconds to delay for CM
;
;	VIDELAY pauses for the number of milliseconds indicated by the A
; register.  VIDELAY assumes a ZCPR3 environment and uses it to determine
; processor speed.
;
VIDELAY:PUSH	PSW		; Save regs
	PUSH	B
	PUSH	D
	PUSH	H
	MOV	C,A		; Save count in C
	ORA	A		; No delay?
	JZ	DONE
	LHLD	ENVPTR		; Pt to environment
	LXI	D,2BH		; Offset to processor speed
	DAD	D
	MOV	A,M		; Get processor speed
	ORA	A		; Zero?
	JNZ	VID1
	MVI	A,4		; Assume 4 MHz
VID1:	MOV	B,A		; Processor speed in B
VID2:	PUSH	B		; Delay 1 ms
	CALL	DELAY
	POP	B
	DCR	C		; Count down
	JNZ	VID2
DONE:	POP	H		; Restore regs
	POP	D
	POP	B
	POP	PSW
	RET
;
;  Delay 1 ms at Clock speed
;
DELAY:	CALL	DEL1		; Delay 1 ms at 1MHz
	DCR	B		; Count down clock speed
	JNZ	DELAY
	RET
;
;  Delay 1 ms at 1MHz
;
DEL1:	MVI	C,20		; 20 loops of 51 cycles each ~ 1000 cycles
DEL1A:	XTHL			; 18 cycles
	XTHL			; +18 = 36 cycles
	DCR	C		; + 5 = 41 cycles
	JNZ	DEL1A		; +10 = 51 cycles
	RET
;
;  VIDSKP - Skip over video string pted to by HL; pt to byte after string
;
VIDSKP:	MOV	A,M		; Get next char
	INX	H		; Pt to next
	ORA	A		; Done if zero
	RZ
	CPI	'\'		; Literal value?
	JNZ	VIDSKP		; Continue if not
	INX	H		; Pt to after literal value
	JMP	VIDSKP
;
;  ZCPR3 Pointer save area
;
VIDPTR:	DS	2		; First byte of termcap entry
ENVPTR:	DS	2
;
BEGMEM	EQU	$
;
	END
